﻿using System;
using System.Runtime.InteropServices;
using System.IO;
using System.Windows.Forms;

//CopyFileEx documentation
//http://msdn.microsoft.com/en-us/library/aa363852%28VS.85%29.aspx

namespace HenIT.IO
{
    public enum CallBackReturnCode
    {
        Continue = 0, //Continue the copy operation.
        Cancel = 1, //Cancel the copy operation and delete the destination file.
        Stop = 2, //Stop the copy operation. It can be restarted at a later time.
        Quiet = 3 //Continue the copy operation, but stop invoking CopyProgressRoutine to report progress.
    }

    public class AsyncFileAction
    {
        private const int COPY_FILE_FAIL_IF_EXISTS = 0x00000001;
        private const int COPY_FILE_NO_BUFFERING = 0x00001000;

        #region DllImport
        [DllImport("kernel32.dll", EntryPoint = "CopyFileExA", SetLastError = true)]
        private static extern Int32 CopyFileEx(
            string lpExistingFileName,
            string lpNewFileName,
            [MarshalAs(UnmanagedType.FunctionPtr)] CopyCallBackDelegate copycallback,
            long lpData,
            bool pbCancel,
            Int32 dwCopyFlags);
        #endregion

        //Visible progress bar
        private FileCopyProgress fileCopyProgress;

        private delegate Int32 CopyCallBackDelegate(uint TotalFileSize, uint BytesTransfered, uint StreamSize, uint StreamBytesTransfered, uint DwStreamNumber, long dwCallbackReason, long hSourceFile, long hDestinationFile, long lpData);

        #region Actions
        public bool CopyFile(string sourceFile, string destinationFile)
        {
            bool success = false;
            try
            {
                if (File.Exists(destinationFile))
                {
                    File.SetAttributes(destinationFile, FileAttributes.Normal);
                    File.Delete(destinationFile);
                }

                fileCopyProgress = new FileCopyProgress();
                fileCopyProgress.TitleText = "Copying " + Path.GetFileName(sourceFile);
                fileCopyProgress.ActionFromText = "Copying " + Path.GetFileName(sourceFile);
                fileCopyProgress.ActionToText = "To " + Path.GetDirectoryName(destinationFile);
                fileCopyProgress.Show();
                System.Windows.Forms.Application.DoEvents();

                CopyCallBackDelegate copycallback = new CopyCallBackDelegate(CopyProgressUpdate);
                if (System.Environment.OSVersion.Version.Major > 5)
                    CopyFileEx(sourceFile, destinationFile, copycallback, 0, false, COPY_FILE_FAIL_IF_EXISTS & COPY_FILE_NO_BUFFERING); // 0x00000001 = FAIL IF FILE EXISTS
                else
                    CopyFileEx(sourceFile, destinationFile, copycallback, 0, false, COPY_FILE_FAIL_IF_EXISTS); // 0x00000001 = FAIL IF FILE EXISTS
                fileCopyProgress.ProgressPercent = 100;

                fileCopyProgress.Close();
                success = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error copying file\r\n" + ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return success;
        }
        public bool MoveFile(string sourceFile, string destinationFile)
        {
            bool success = false;
            try
            {
                if (File.Exists(sourceFile) && File.Exists(destinationFile)) //only delete if both exist
                {
                    File.SetAttributes(destinationFile, FileAttributes.Normal);
                    File.Delete(destinationFile);
                }

                fileCopyProgress = new FileCopyProgress();
                fileCopyProgress.TitleText = "Moving " + Path.GetFileName(sourceFile);
                fileCopyProgress.ActionFromText = "Moving " + Path.GetFileName(sourceFile);
                fileCopyProgress.ActionToText = "To " + Path.GetDirectoryName(destinationFile);
                fileCopyProgress.Show();
                System.Windows.Forms.Application.DoEvents();

                CopyCallBackDelegate copycallback = new CopyCallBackDelegate(CopyProgressUpdate);
                if (System.Environment.OSVersion.Version.Major > 5)
                    CopyFileEx(sourceFile, destinationFile, copycallback, 0, false, COPY_FILE_FAIL_IF_EXISTS & COPY_FILE_NO_BUFFERING); // 0x00000001 = FAIL IF FILE EXISTS
                else
                    CopyFileEx(sourceFile, destinationFile, copycallback, 0, false, COPY_FILE_FAIL_IF_EXISTS); // 0x00000001 = FAIL IF FILE EXISTS
                fileCopyProgress.ProgressPercent = 100;

                fileCopyProgress.Close();

                if (!fileCopyProgress.Aborted)
                {
                    File.SetAttributes(sourceFile, FileAttributes.Normal);
                    File.Delete(sourceFile);
                }
                success = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error copying file\r\n" + ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return success;
        }
        #endregion

        #region Call back
        private Int32 CopyProgressUpdate(uint TotalFileSize, uint BytesTransfered, uint StreamSize, uint StreamBytesTransfered, uint DwStreamNumber, long dwCallbackReason, long hSourceFile, long hDestinationFile, long lpData)
        {
            if (!fileCopyProgress.Aborted)
            {
                // This takes the current stream size (amount transfered) and creates a percentage based on file size
                fileCopyProgress.ProgressPercent = Convert.ToInt32(((float)StreamSize / (float)TotalFileSize) * 100);
                fileCopyProgress.SetKBCopied((int)(StreamSize / 1024));
                return (Int32)CallBackReturnCode.Continue;
            }
            else //abort
            {
                return (Int32)CallBackReturnCode.Cancel;
            }
        }
        #endregion
    }
}
